{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Running the Quantum Volume Algorithm\n",
    "This example walks through the steps of running the quantum volume algorithm on square matrices. It is intended to mirror Algorithm 1 of https://arxiv.org/pdf/1811.12926.pdf. In general, we will generate a model circuit, classically compute its Heavy Output Group, then run various samplers (currently, ideal and noisy simulators) to evaluate how often they generate Heavy results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# This cell sets up the parameters for the quantum volume algorithm.\n",
    "# Feel free to mess with these!\n",
    "import cirq\n",
    "\n",
    "num_repetitions = 10  # This is supposed to be >= 100.\n",
    "depths = range(2, 5)  # The depths and number of qubits\n",
    "optimize = lambda circuit: cirq.google.optimized_for_xmon(\n",
    "    circuit=circuit,\n",
    "    new_device=cirq.google.Bristlecone)\n",
    "device = cirq.google.Bristlecone\n",
    "\n",
    "# Here is the important set-up: the samplers and their plot configurations.\n",
    "# These are what will be run on the generated circuit, and then evaluated.\n",
    "samplers = [{\n",
    "    'label': 'Ideal simulation',\n",
    "    'sampler': cirq.Simulator(),\n",
    "    'marker': '+',\n",
    "    'color': 'tab:green',\n",
    "}, {\n",
    "    'label': 'Noisy simulation',\n",
    "    'sampler':\n",
    "    cirq.DensityMatrixSimulator(noise=cirq.ConstantQubitNoiseModel(\n",
    "        qubit_noise_gate=cirq.DepolarizingChannel(p=0.005))),\n",
    "    'color': 'tab:red',\n",
    "    'marker': 'o',\n",
    "}]\n",
    "\n",
    "print(f\"Configuration: depths from {depths[0]} to {depths[-1]} with \"\n",
    "      f\"{num_repetitions} runs of {len(samplers)} samplers\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# This cell contains the business logic that actually runs the quantum volume algorithm with\n",
    "# parameters specified in the previous cell.\n",
    "from cirq.contrib import quantum_volume\n",
    "from collections import defaultdict\n",
    "import numpy as np\n",
    "\n",
    "for sampler in samplers:\n",
    "    sampler['probabilities'] = defaultdict(int)\n",
    "    sampler['routed-probabilities'] = defaultdict(int)\n",
    "    sampler['compiled-probabilities'] = defaultdict(int)\n",
    "\n",
    "for depth in depths:\n",
    "    num_qubits = depth  # Square matrix.\n",
    "    print(f\"Running simulation with {num_qubits} qubits and a depth of {depth}\")\n",
    "    for i in range(num_repetitions):\n",
    "        print(f\"    Repetition {i + 1} of {num_repetitions}\")\n",
    "        # Generate a model circuit and compute its heavy set.\n",
    "        model_circuit = quantum_volume.generate_model_circuit(\n",
    "            num_qubits, depth, random_state=np.random.RandomState())\n",
    "        heavy_set = quantum_volume.compute_heavy_set(model_circuit)\n",
    "        print(f\"        Heavy Set: {heavy_set}\")\n",
    "\n",
    "        # Route and compile the model circuit.\n",
    "        [routed_circuit,\n",
    "         mapping] = quantum_volume.compile_circuit(model_circuit, device=device, routing_attempts=1)\n",
    "        compiled_circuit = optimize(routed_circuit)\n",
    "\n",
    "        # Run the given samplers over the model, compiled, and optimized circuits.\n",
    "        for sampler in samplers:\n",
    "            probability = quantum_volume.sample_heavy_set(\n",
    "                model_circuit, heavy_set, sampler=sampler['sampler'])\n",
    "            sampler['probabilities'][depth] += probability\n",
    "            print(f\"        {sampler['label']} HOG probability: {probability}\")\n",
    "\n",
    "            routed_probability = quantum_volume.sample_heavy_set(\n",
    "                routed_circuit,\n",
    "                heavy_set,\n",
    "                sampler=sampler['sampler'],\n",
    "                mapping=mapping)\n",
    "            sampler['routed-probabilities'][depth] += routed_probability\n",
    "            print(\n",
    "                f\"        {sampler['label']} HOG routed probability: {routed_probability}\"\n",
    "            )\n",
    "\n",
    "            compiled_probability = quantum_volume.sample_heavy_set(\n",
    "                compiled_circuit,\n",
    "                heavy_set,\n",
    "                sampler=sampler['sampler'],\n",
    "                mapping=mapping)\n",
    "            sampler['compiled-probabilities'][depth] += compiled_probability\n",
    "            print(\n",
    "                f\"        {sampler['label']} HOG compiled probability: {compiled_probability}\"\n",
    "            )\n",
    "\n",
    "    # Compute the average performance over the total number of runs.\n",
    "    for sampler in samplers:\n",
    "        sampler['probabilities'][depth] /= num_repetitions\n",
    "        sampler['routed-probabilities'][depth] /= num_repetitions\n",
    "        sampler['compiled-probabilities'][depth] /= num_repetitions\n",
    "        print(f\"    Average {sampler['label']} HOG probability: \"\n",
    "              f\"{sampler['probabilities'][depth]}\")\n",
    "        print(f\"    Average {sampler['label']} optimized HOG probability: \"\n",
    "              f\"{sampler['routed-probabilities'][depth]}\")\n",
    "        print(f\"    Average {sampler['label']} compiled HOG probability: \"\n",
    "              f\"{sampler['compiled-probabilities'][depth]}\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create a chart that is designed to look as similar as possible to\n",
    "# Figure 2 in https://arxiv.org/pdf/1811.12926.pdf.\n",
    "\n",
    "from matplotlib import pyplot as plt\n",
    "\n",
    "fig, axs = plt.subplots()\n",
    "for idx, sampler in enumerate(samplers):\n",
    "    axs.scatter([d + idx / 10 for d in depths],\n",
    "                sampler['probabilities'].values(),\n",
    "                marker='+',\n",
    "                c=f\"{sampler['color']}\",\n",
    "                label=f\"{sampler['label']}\")\n",
    "    axs.scatter([d + idx / 10 for d in depths],\n",
    "                sampler['routed-probabilities'].values(),\n",
    "                marker=\"o\",\n",
    "                c=f\"{sampler['color']}\",\n",
    "                label=f\"{sampler['label']} Routed\")\n",
    "    axs.scatter([d + idx / 10 for d in depths],\n",
    "                sampler['compiled-probabilities'].values(),\n",
    "                marker=\"s\",\n",
    "                c=f\"{sampler['color']}\",\n",
    "                label=f\"{sampler['label']} Compiled\")\n",
    "\n",
    "# Line markers for asymptotic ideal heavy output probability and the ideal\n",
    "# Heavy Output Generation threshold.\n",
    "axs.axhline((1 + np.log(2)) / 2,\n",
    "            color='tab:green',\n",
    "            label='Asymptotic ideal',\n",
    "            linestyle='dashed')\n",
    "axs.axhline(2 / 3, label='HOG threshold', color='k', linestyle='dotted')\n",
    "# Making the plot look consistent.\n",
    "axs.set_ybound(0.0, 1)\n",
    "axs.xaxis.set_major_locator(plt.MultipleLocator(1))\n",
    "axs.set_xlabel(\"width/depth of model circuit m=d\")\n",
    "axs.set_ylabel(\"est. heavy output probability h(d)\")\n",
    "fig.suptitle('Experimental data for square quantum volume circuits')\n",
    "axs.legend(loc='best')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
